home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / ast_comp / cpp-kit.lha / c++kit / Parse.txt < prev    next >
Text File  |  1993-04-11  |  6KB  |  194 lines

  1. I've been trying to put together a parser/scanner toolkit in C++ that emphasises
  2. ease of use, rather than optimality.
  3.  
  4. BINDING:
  5.  
  6. To start parsing a file, either specify it when creating the Parse object,
  7. or open it.
  8.  
  9.     Parse P("file");
  10.  
  11.     Parse P;
  12.        P.open("file");
  13.  
  14. The number of lines parsed so far can be obtained by the call
  15.        cerr << "line #" << P.line();
  16.  
  17. MATCHING:
  18.  
  19. Every ``match'' of the parser declares a string which the Parse object, P,
  20. should try to match in the file being parsed. While it succeeds (int) P is
  21. 1. If at some point it fails, (int) P is 0. There are mechanisms for
  22. specifying conjunction (and--represented by juxtaposition of ``matches'')
  23. and disjunction (or--represented by an OR. This will also force backtracking).
  24. There are primitive, pre-defined ``matches''. Finally, there are
  25. user-defined ``matches''.
  26.  
  27. For the following section assume that the variable P has been declared as
  28.     Parse P;
  29.  
  30. The Primitive Matches:
  31. The following two matches specify precise matches
  32.  
  33.     -- match character: matches specified character
  34.          P , 'x';
  35.  
  36.    --  match string: matches specified string, first skipping any white-space.
  37.      P , "alpha";
  38.  
  39.    -- match end of file: self explanatory
  40.      P , EOFL;
  41.  
  42. The rest of the primitives match some string, and return a Token.
  43. A Token is basically a (possibly not null terminated) string and length pair.
  44. Assume that there is the following declaration:
  45.  
  46.          Token t;
  47.  
  48. Further, all but match character skip all leading white-space.
  49.  
  50.    -- match number: matches string [-+]?[0-9]+
  51.      P , NUMBER(t);
  52.    
  53.    -- match string: matches "([^\\\n"]|(\.))*"
  54.      P , STRING(t);
  55.    
  56.    -- match ident: matches [_a-zA-Z][_a-zA-Z0-9]*
  57.      P , IDENT(t);
  58.    
  59.    -- match token: matches a string or ident, except that it returns a string
  60.       without the quotes.
  61.      P , TOKEN(t);
  62.    
  63.    -- match character: matches next character (including whitespace)
  64.      P , CHARAC(t);
  65.    
  66. And'ing Matches:
  67.       
  68. Simply writing any number of matches next to each other (with ,s in between) is
  69. the same as and'ing. This is written as:
  70.      P, match1, match2 ,..., matchN;
  71.  
  72. The Parse object will try to find a match only if all
  73. preceding matches have succeeded. The Parse will match the and'ed matches
  74. if and only if it matches all of the matches.
  75.  
  76.       Token id, val;
  77.          P, IDENT(id), "=", NUMBER(val);
  78.  
  79. This example will match a string like 'X12 =  -12'
  80.  
  81. Or'ing Matches:
  82.  
  83. Trying to match one of two or more match sequences is written as
  84.  
  85.       P, and1, OR, and2, OR, ..., OR, andN ;
  86.  
  87. The Parse object will try to match an and sequence if and only if it
  88. has failed all preceding and sequences. It begins each and sequence
  89. at the same point. 
  90.  
  91.       Token id, val;
  92.          P, IDENT(id), "=", NUMBER(val),
  93.      OR, IDENT(id), ":", STRING(val) ;
  94.  
  95. This example will match string 'Xa: "alpha"'
  96.  
  97. Of course, this would not be very useful just as it is, since it would
  98. be difficult to find which and was actually matched. So there is a
  99. primitive CHOOSE(N) (N is an integer) which always succeeds.
  100. If the Parse object tries to match with a CHOOSE, it always succeeds,
  101. and saves N. The value stored by the last CHOOSE of a (successful) Parse
  102. object P can be retrieved by the member function (). If the Parse
  103. was unsuccessful (i.e. if none of the ands matched) it returns 0.
  104.  
  105.       Token id, val;
  106.          P, IDENT(id), "=", NUMBER(val), CHOOSE(1),
  107.      OR, IDENT(id), ":", STRING(val), CHOOSE(2) ;
  108.      switch(P()) {
  109.      case 0: {
  110.            cerr << "no match" << endl;
  111.            break;
  112.         }
  113.      case 1: {
  114.            cerr << "number match" << endl;
  115.            break;
  116.         }
  117.      case 2: {
  118.            cerr << "string match" << endl;
  119.            break;
  120.         }
  121.      }
  122.  
  123. User-defined Matches:
  124.  
  125. The real power of this system comes from the ability of the user to define
  126. functions that can been be used to do more powerful matches.
  127.  
  128. A user-defined match function has one of the following signatures:
  129.       int func(Parse&);
  130.       int func(Parse&, T&);
  131.       int func(Parse&, S&, T&);
  132.  
  133. It returns a 0 if the match fails. S and T are just parameters used
  134. to return values. Consider a function that matches an identifier
  135. only if it has been defined before. Also, if it does find the
  136. identifier, it returns the pointer to its symbol table entry.
  137.  
  138.       int name(Parse& P, StbEntry *& stb)
  139.       {
  140.       Token  t;
  141.  
  142.       P, IDENT(t);
  143.       if( P && (stb = StbFind(t.string(), t.length())) != 0 ) {
  144.          return 1;
  145.       }
  146.       return 0;
  147.       }
  148.  
  149. These functions are invoked as follows:
  150.       P , MATCH(func);
  151.       P , MATCH(func, var);
  152.       P , MATCH(func, var, var);
  153.  
  154. So, the name function can be used as follows:
  155.  
  156.      Token      t;
  157.      StbEntry * var;
  158.     P, MATCH(name, var), "=", NUMBER(val), CHOOSE(1),
  159.     OR, IDENT(t), "=", NUMBER(val), CHOOSE(2);
  160.  
  161. Notice that the first and is matched _ONLY_ if name is matched (i.e. returns 1),
  162. which means that it is matched only if the ident has been entered in the
  163. symbol table earlier.
  164.  
  165. Some Common Idioms:
  166.  
  167. If we want zero or more of a particular match, we use recursion:
  168.  
  169.       int foostar(Parse& P)
  170.       {
  171.          P , MATCH(foo) , MATCH(foostar),
  172.          OR /* nothing */;
  173.      return P;
  174.       }
  175.  
  176. To read an entire line
  177.        int getline(Parse& P)
  178.        {
  179.        Token t;
  180.        do {
  181.           P, CHAR(t);
  182.        } while(t[0] != '\n' );
  183.        }
  184.  
  185.  
  186. BACKTRACKING:
  187.  
  188. Backtracking takes place every time an OR is encountered, and no prior
  189. and sequence has been matched. Parsing resumes at the point where
  190. the input was on entry to the function. This is why matching a list
  191. of items requires recursion, and not iteration.
  192.  
  193. The number of lines parsed is reset on a backtrack.
  194.